﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;
using System.Configuration;
using System.Globalization;

/// <summary>
/// Form Validation Class
/// </summary>
public class FormValidation
{
    StringBuilder sbMessage = new StringBuilder();
    List<FormFields> fields = new List<FormFields>();
    public FormValidation() { 
    
    }

    /// <summary>
    /// Register a field
    /// </summary>
    /// <param name="name">Field name</param>
    /// <param name="title">Field label</param>
    /// <param name="value">Field value</param>
    /// <param name="validations">Comma separated value of these validation keys : Required,LimitLength,Number,AlphaNumeric,Date,Time,Regex</param>
    public void RegisterField(string name, string title, string value, string validations)
    {
        RegisterField(name, title, value, validations, 0, "", "");
    }

    /// <summary>
    /// Register a field
    /// </summary>
    /// <param name="name">Field name</param>
    /// <param name="title">Field label</param>
    /// <param name="value">Field value</param>
    /// <param name="validations">Comma separated value of these validation keys : Required,LimitLength,Number,AlphaNumeric,Date,Time,Regex</param>
    /// <param name="limit">Maximum string length. Required only for LimitLength validation</param>
    public void RegisterField(string name, string title, string value, string validations, int limit)
    {
        RegisterField(name, title, value, validations, limit, "", "");
    }

    /// <summary>
    /// Register a field
    /// </summary>
    /// <param name="name">Field name</param>
    /// <param name="title">Field label</param>
    /// <param name="value">Field value</param>
    /// <param name="validations">Comma separated value of these validation keys : Required,LimitLength,Number,AlphaNumeric,Date,Time,Regex</param>
    /// <param name="limit">Maximum string length. Required only for LimitLength validation</param>
    /// <param name="regex">Regex string. Required only for Regex validation</param>
    public void RegisterField(string name, string title, string value, string validations, int limit, string regex)
    {
        RegisterField(name, title, value, validations, limit, regex, "");
    }

    /// <summary>
    /// Register a field
    /// </summary>
    /// <param name="name">Field name</param>
    /// <param name="title">Field label</param>
    /// <param name="value">Field value</param>
    /// <param name="validations">Comma separated value of these validation keys : Required,LimitLength,Number,AlphaNumeric,Date,Time,Regex</param>
    /// <param name="regex">Regex string. Required only for Regex validation</param>
    /// <param name="limit">Maximum string length. Required only for LimitLength validation</param>
    /// <param name="fieldNameToCompare">Field name to compare</param>
    public void RegisterField(string name, string title, string value, string validations, int limit, string regex, string fieldNameToCompare)
    {
        string[] vals = validations.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
        List<FormFields.ValidationType> valtypes = new List<FormFields.ValidationType>();
        foreach (string item in vals)
        {
            try
            {
                valtypes.Add((FormFields.ValidationType)Enum.Parse(typeof(FormFields.ValidationType), item));
            }
            catch { }
        }
        FormFields fld = new FormFields();
        fld.Name = name;
        fld.Title = title;
        fld.Value = value;
        fld.Validations = valtypes;
        fld.Regex = regex;
        fld.Length = limit;
        fld.CompareFieldName = fieldNameToCompare;
        fields.Add(fld);
    }

    /// <summary>
    /// Initiate field validations. Must be called after registering fields. 
    /// </summary>
    public void ValidateFields()
    {
        StringBuilder errorMsg = new StringBuilder();
        foreach (FormFields item in fields)
        {
            foreach (FormFields.ValidationType type in item.Validations)
            {
                switch (type)
                {
                    case FormFields.ValidationType.Required:
                        if (string.IsNullOrEmpty(item.Message)) {
                            if (!ValidateRequired(item)) item.Message = "Field "+item.Title+" is required.";
                        }
                        break;
                    case FormFields.ValidationType.LimitLength:
                        if (string.IsNullOrEmpty(item.Message)) {
                            if (!ValidateLength(item)) item.Message = "Field " + item.Title + " is limited to " + item.Length.ToString() + " characters only.";
                        }
                        break;
                    case FormFields.ValidationType.Number:
                        if (string.IsNullOrEmpty(item.Message)) {
                            if (!ValidateNumeric(item)) item.Message = "Field "+item.Title+" is invalid. Please enter numeric characters.";
                        }
                        break;
                    case FormFields.ValidationType.AlphaNumeric:
                        if (string.IsNullOrEmpty(item.Message)) {
                            if (!ValidateAlphaNumeric(item)) item.Message = "Field "+item.Title+" is invalid. Please enter alphanumeric characters.";
                        }
                        break;
                    case FormFields.ValidationType.Date:
                        if (string.IsNullOrEmpty(item.Message)) {
                            if (!ValidateDate(item)) item.Message = "Field " + item.Title + " is invalid. Please enter a date using 'dd/mm/yyyy' format.";
                        }
                        break;
                    case FormFields.ValidationType.Time:
                        if (string.IsNullOrEmpty(item.Message)) {
                            if (!ValidateTime(item)) item.Message = "Field " + item.Title + " is invalid. Please enter a time using 'hh:mm:ss' format.";
                        }
                        break;
                    case FormFields.ValidationType.Regex:
                        if (string.IsNullOrEmpty(item.Message)) {
                            if (!ValidateRegex(item)) item.Message = "Field " + item.Title + " is invalid.";
                        }
                        break;
                    default:
                        break;
                }
            }

        }
        RebuildMessage();
    }

    /// <summary>
    /// Check form validation for errors. Must be called after ValidateFields method.
    /// </summary>
    /// <returns>True if no error found.</returns>
    public bool IsValid {
        get
        {
            return string.IsNullOrEmpty(sbMessage.ToString());
        }
    }

    /// <summary>
    /// Append custom error message if current field name has no error message.
    /// </summary>
    /// <param name="message">Custom error message</param>
    /// <param name="fieldname">Field Name</param>
    public void AddCustomError(string fieldname, string message) {
        FormFields mf =  fields.Find(i => i.Name == fieldname);
        if ((mf!=null) && (string.IsNullOrEmpty(mf.Message))) {
            mf.Message = message;
        }
        RebuildMessage();
    }

    /// <summary>
    /// Rebuild error message list
    /// </summary>
    void RebuildMessage()
    {
        sbMessage = new StringBuilder();
        StringBuilder sb = new StringBuilder();
        fields.ForEach(delegate(FormFields f)
        {
            if (!string.IsNullOrEmpty(f.Message))
            {
                sb.Append("<li>" + f.Message + "</li>");
            }
        });
        if (!string.IsNullOrEmpty(sb.ToString()))
        {
            sbMessage.Append("<ul class=\"errorList\">" + sb.ToString() + "</ul>");
        }
    }

    /// <summary>
    /// Get comma separated field names which raise errors
    /// </summary>
    /// <param name="fieldNames">Additional field names. Comma separated.</param>
    /// <returns>Comma separated field names</returns>
    public string GetErrorFields(string fieldNames) {
        StringBuilder sb = new StringBuilder();
        foreach (FormFields item in fields)
        {
            if (!string.IsNullOrEmpty(item.Message)) {
                if (!string.IsNullOrEmpty(sb.ToString())) sb.Append(",");
                sb.Append(item.Name);
            }
        }

        if (!string.IsNullOrEmpty(sb.ToString())) sb.Append(",");
        sb.Append(fieldNames);
        return sb.ToString();
    }

    /// <summary>
    /// Get registered field
    /// </summary>
    /// <param name="fieldName">Field name</param>
    /// <returns>Field object</returns>
    public FormFields GetField(string fieldName)
    {
        return fields.FirstOrDefault(i => i.Name == fieldName);
    }

    /// <summary>
    /// Get error message list
    /// </summary>
    /// <param name="useJsonFormat">Set to true to get JSON formatted error list</param>
    /// <returns>Error message list</returns>
    public string GetErrorMessages(bool useJsonFormat) {
        if (useJsonFormat) {
            StringBuilder sbJson = new StringBuilder();
            fields.ForEach(delegate(FormFields f) {
                if (!string.IsNullOrEmpty(f.Message)) {
                    if (!string.IsNullOrEmpty(sbJson.ToString())) sbJson.Append(",");
                    sbJson.Append("{\"f\":\""+f.Name+"\",\"m\":\""+f.Message+"\"}");
                }
            });
            return "["+sbJson.ToString()+"]";
        } else return sbMessage.ToString();
    }

    public bool ValidateRequired(FormFields field)
    {
        return (field!=null) && !string.IsNullOrEmpty(field.Value.Trim());
    }
    public bool ValidateNumeric(FormFields field)
    {
        bool ret = false;
        try
        {
            int tmp = 0;
            int.TryParse(field.Value, out tmp);
            ret = tmp.ToString() == field.Value;
        }
        catch { ret = false; }
        return ret;
    }
    public bool ValidateAlphaNumeric(FormFields field)
    {
        Regex rg = new Regex("[^a-zA-Z0-9]");
        //if has non AlpahNumeric char, return false, else return true.
        return !rg.IsMatch(field.Value);
    }
    public bool ValidateDate(FormFields field)
    {
        string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
        DateTime Test;
        return (DateTime.TryParseExact(field.Value, format, null, DateTimeStyles.None, out Test) == true);
    }
    public bool ValidateTime(FormFields field)
    {
        bool ret = false;
        try
        {
            string[] tm = field.Value.Split(new string[] { ":" }, StringSplitOptions.RemoveEmptyEntries);
            int hour = tm.Length > 0 ? int.Parse(tm[0]) : 0;
            int minute = tm.Length > 1 ? int.Parse(tm[1]) : 0;
            int second = tm.Length > 2 ? int.Parse(tm[2]) : 0;
            ret = ((hour >= 0) && (hour < 24) && (minute >= 0) && (minute < 60) && (second >= 0) && (second < 60));
        }
        catch { ret = false; }
        return ret;
    }
    public bool ValidateRegex(FormFields field)
    {
        Regex rg = new Regex(field.Regex);
        return rg.IsMatch(field.Value);
    }
    public bool ValidateLength(FormFields field)
    {
        return field.Value.Length <= field.Length;
    }

    public bool DateLowerThan(FormFields field, DateTime dateToCompare)
    {
        try
        {
            string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
            DateTime value1 = DateTime.ParseExact(field.Value, format, null, DateTimeStyles.None);
            return value1 < dateToCompare;
        }
        catch
        {
            return false;
        }
    }
    public bool DateLowerThan(FormFields field, FormFields fieldToCompare)
    {
        try
        {
            string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
            DateTime value1 = DateTime.ParseExact(field.Value, format, null, DateTimeStyles.None);
            DateTime value2 = DateTime.ParseExact(fieldToCompare.Value, format, null, DateTimeStyles.None);
            return value1 < value2;
        }
        catch
        {
            return false;
        }
    }
    public bool DateLowerOrEqualThan(FormFields field, DateTime dateToCompare)
    {
        try
        {
            string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
            DateTime value1 = DateTime.ParseExact(field.Value, format, null, DateTimeStyles.None);
            return value1 <= dateToCompare;
        }
        catch
        {
            return false;
        }
    }
    public bool DateLowerOrEqualThan(FormFields field, FormFields fieldToCompare)
    {
        try
        {
            string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
            DateTime value1 = DateTime.ParseExact(field.Value, format, null, DateTimeStyles.None);
            DateTime value2 = DateTime.ParseExact(fieldToCompare.Value, format, null, DateTimeStyles.None);
            return value1 <= value2;
        }
        catch
        {
            return false;
        }
    }
    public bool DateGreaterThan(FormFields field, DateTime dateToCompare)
    {
        try
        {
            string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
            DateTime value1 = DateTime.ParseExact(field.Value, format, null, DateTimeStyles.None);
            return value1 > dateToCompare;
        }
        catch
        {
            return false;
        }
    }
    public bool DateGreaterThan(FormFields field, FormFields fieldToCompare)
    {
        try
        {
            string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
            DateTime value1 = DateTime.ParseExact(field.Value, format, null, DateTimeStyles.None);
            DateTime value2 = DateTime.ParseExact(fieldToCompare.Value, format, null, DateTimeStyles.None);
            return value1 > value2;
        }
        catch
        {
            return false;
        }
    }
    public bool DateGreaterOrEqualThan(FormFields field, DateTime dateToCompare)
    {
        try
        {
            string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
            DateTime value1 = DateTime.ParseExact(field.Value, format, null, DateTimeStyles.None);
            return value1 >= dateToCompare;
        }
        catch
        {
            return false;
        }
    }
    public bool DateGreaterOrEqualThan(FormFields field, FormFields fieldToCompare)
    {
        try
        {
            string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
            DateTime value1 = DateTime.ParseExact(field.Value, format, null, DateTimeStyles.None);
            DateTime value2 = DateTime.ParseExact(fieldToCompare.Value, format, null, DateTimeStyles.None);
            return value1 >= value2;
        }
        catch
        {
            return false;
        }
    }
    public bool DateEqual(FormFields field, DateTime dateToCompare)
    {
        try
        {
            string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
            DateTime value1 = DateTime.ParseExact(field.Value, format, null, DateTimeStyles.None);
            return value1 == dateToCompare;
        }
        catch
        {
            return false;
        }
    }
    public bool DateEqual(FormFields field, FormFields fieldToCompare)
    {
        try
        {
            string format = ConfigurationManager.AppSettings["_CONFIG_DATE_FORMAT"];
            DateTime value1 = DateTime.ParseExact(field.Value, format, null, DateTimeStyles.None);
            DateTime value2 = DateTime.ParseExact(fieldToCompare.Value, format, null, DateTimeStyles.None);
            return value1 == value2;
        }
        catch
        {
            return false;
        }
    }

    public bool NumberLowerThan(FormFields field, FormFields fieldToCompare)
    {
        try
        {
            Decimal value1 = Decimal.Parse(field.Value);
            Decimal value2 = Decimal.Parse(fieldToCompare.Value);
            return value1 < value2;
        }
        catch
        {
            return false;
        }
    }
    public bool NumberLowerOrGreaterThan(FormFields field, FormFields fieldToCompare)
    {
        try
        {
            Decimal value1 = Decimal.Parse(field.Value);
            Decimal value2 = Decimal.Parse(fieldToCompare.Value);
            return value1 <= value2;
        }
        catch
        {
            return false;
        }
    }
    public bool NumberGreaterThan(FormFields field, FormFields fieldToCompare)
    {
        try
        {
            Decimal value1 = Decimal.Parse(field.Value);
            Decimal value2 = Decimal.Parse(fieldToCompare.Value);
            return value1 > value2;
        }
        catch
        {
            return false;
        }
    }
    public bool NumberGreaterOrEqualThan(FormFields field, FormFields fieldToCompare)
    {
        try
        {
            Decimal value1 = Decimal.Parse(field.Value);
            Decimal value2 = Decimal.Parse(fieldToCompare.Value);
            return value1 >= value2;
        }
        catch
        {
            return false;
        }
    }
    public bool NumberEqual(FormFields field, FormFields fieldToCompare)
    {
        try
        {
            Decimal value1 = Decimal.Parse(field.Value);
            Decimal value2 = Decimal.Parse(fieldToCompare.Value);
            return value1 == value2;
        }
        catch
        {
            return false;
        }
    }

}

/// <summary>
/// Form Field class for validation
/// </summary>
public class FormFields
{
    public enum ValidationType
    {
        Required = 0,
        LimitLength,
        Number,
        AlphaNumeric,
        Date,
        Time,
        Regex
    }
    public string Name { get; set; }
    public string Title { get; set; }
    public string Value { get; set; }
    public string Regex { get; set; }
    public int Length { get; set; }
    public string CompareFieldName { get; set; }
    public List<ValidationType> Validations { get; set; }
    public string Message { get; set; }
    public bool IsValid {
        get {
            return string.IsNullOrEmpty(Message);
        }
    }
}
